package com.mall.shopping.services;

import com.mall.shopping.ICartService;
import com.mall.shopping.constants.ShoppingRetCode;
import com.mall.shopping.dal.persistence.ItemMapper;
import com.mall.shopping.dto.*;
import com.mall.shopping.dto.CartProductDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Slf4j
@Service
@Component
public class CartServiceImpl implements ICartService {

    private static final String KEY_PREFIX = "cart_user_id_";
    private static final String CHECKED_TRUE = "true";
    private static final BatchOptions BATCH_OPTIONS = BatchOptions.defaults().executionMode(BatchOptions.ExecutionMode.IN_MEMORY);

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    ItemMapper itemMapper;

    @Override
    public CartListByIdResponse getCartListById(CartListByIdRequest request) {
        RMap<Long, CartProductDto> cartMap = redissonClient.getMap(KEY_PREFIX + request.getUserId());
        List<CartProductDto> list = new ArrayList<>();
        Set<Map.Entry<Long, CartProductDto>> entries = cartMap.readAllEntrySet();
        if (entries == null) {
            list = null;
        } else {
            for (Map.Entry<Long, CartProductDto> e : entries) {
                list.add(e.getValue());
            }
        }
        CartListByIdResponse response = new CartListByIdResponse();
        response.setCartProductDtos(list);
        response.setCode(ShoppingRetCode.SUCCESS.getCode());
        response.setMsg(ShoppingRetCode.SUCCESS.getMessage());
        return response;
    }

    @Override
    public AddCartResponse addToCart(AddCartRequest request) {
        RMap<Long, CartProductDto> cartMap = redissonClient.getMap(KEY_PREFIX + request.getUserId());
        CartProductDto dto = cartMap.get(request.getItemId());
        if (dto == null) {
            dto = itemMapper.selectItemById(request.getItemId());
            if (request.getNum() > dto.getLimitNum()) {
                dto.setProductNum(dto.getLimitNum());
            } else {
                dto.setProductNum(Long.valueOf(request.getNum()));
            }
        } else {
            if (request.getNum() > dto.getLimitNum()) {
                dto.setProductNum(dto.getLimitNum() + dto.getProductNum());
            } else {
                dto.setProductNum(Long.valueOf(request.getNum()) + dto.getProductNum());
            }
        }
        cartMap.put(request.getItemId(), dto);
        AddCartResponse response = new AddCartResponse();
        response.setCode(ShoppingRetCode.SUCCESS.getCode());
        response.setMsg(ShoppingRetCode.SUCCESS.getMessage());
        return response;
    }

    @Override
    public UpdateCartNumResponse updateCartNum(UpdateCartNumRequest request) {
        UpdateCartNumResponse response = new UpdateCartNumResponse();

        RMap<Long, CartProductDto> cartMap = redissonClient.getMap(KEY_PREFIX + request.getUserId());
        CartProductDto cartProductDto = cartMap.get(request.getProductId());
        if ("1".equals(request.getChecked()) || cartProductDto.getChecked().equals(request.getChecked())) { //更新数量
            if (updateCartNum(request, response, cartMap)) return response; //更新失败
        } else { //选中或取消
            cartProductDto.setChecked(request.getChecked());
            cartMap.put(request.getProductId(), cartProductDto);
        }

        response.setCode(ShoppingRetCode.SUCCESS.getCode());
        response.setMsg(ShoppingRetCode.SUCCESS.getMessage());

        return response;
    }

    private boolean updateCartNum(UpdateCartNumRequest request, UpdateCartNumResponse response, RMap<Long, CartProductDto> cartMap) {
        String sql = null;
        Integer productNum = request.getProductNum();
        Long productId = request.getProductId();

        //获取商品库存
        sql = "select `stock_count`, `restrict_count` from `tb_stock` where `item_id` = ?";
        Map<String, Object> stockResult = jdbcTemplate.queryForMap(sql, productId);
        Long stockCount = (Long) stockResult.get("stock_count");
        Integer restrictCount = (Integer) stockResult.get("restrict_count");

        //获取已购买数量
        sql = "select sum(`num`) as `bought` from `tb_order_item` " +
                "join `tb_order` on `tb_order_item`.`order_id` = `tb_order`.`order_id` " +
                "where `tb_order`.`user_id` = ? and `tb_order_item`.`item_id` = ?";
        Integer boughtNumber = jdbcTemplate.queryForObject(sql, new Object[]{request.getUserId(), productId}, Integer.class);
        if (null == boughtNumber) {
            boughtNumber = 0;
        }

        //判断库存, ///此处不用BigInteger/////bigInteger.intValue()有溢出的可能, 不使用
        if (stockCount - productNum < 0) {
            response.setCode(ShoppingRetCode.OUT_OF_STOCK.getCode());
            response.setMsg(ShoppingRetCode.OUT_OF_STOCK.getMessage());
            return true;
        }
        //判断限购
        if (!(0 == restrictCount) && restrictCount < boughtNumber + productNum) {
             response.setCode(ShoppingRetCode.RESTRICT_COUNT_REACHED.getCode());
            response.setMsg(ShoppingRetCode.RESTRICT_COUNT_REACHED.getMessage());
            return true;
        }
        //productNum小于等于零则置为1
        if (0 >= productNum) {
            CartProductDto cartProductDto = cartMap.get(productId);
            cartProductDto.setProductNum(1L);
            cartMap.put(productId, cartProductDto);
            return false;
        }

        //更新redis
        CartProductDto cartProductDto = cartMap.get(productId);
        cartProductDto.setProductNum(Long.valueOf(productNum));
        cartProductDto.setLimitNum(Long.valueOf(restrictCount));
        cartMap.put(productId, cartProductDto);
        return false;
    }

    @Override
    public CheckAllItemResponse checkAllCartItem(CheckAllItemRequest request) {
        CheckAllItemResponse response = new CheckAllItemResponse();
        String checked = request.getChecked();

        //获取所有内容
        RMap<Long, CartProductDto> cartMapRead = redissonClient.getMap(KEY_PREFIX + request.getUserId());
        //创建batch用于批量写入
        RBatch batch = redissonClient.createBatch(BATCH_OPTIONS);
        //创建异步RMap用于批量写入
        RMapAsync<Object, Object> cartMapWriteAsync = batch.getMap(KEY_PREFIX + request.getUserId());
        for (Map.Entry<Long, CartProductDto> e : cartMapRead.entrySet()) {
            CartProductDto value = e.getValue();
            value.setChecked(checked);
            //此时没有写入,等到执行batch.execute()才写入
            cartMapWriteAsync.fastPutAsync(e.getKey(), value);
        }
        batch.execute();

        response.setCode(ShoppingRetCode.SUCCESS.getCode());
        response.setMsg(ShoppingRetCode.SUCCESS.getMessage());

        return response;
    }

    @Override
    public DeleteCartItemResponse deleteCartItem(DeleteCartItemRequest request) {
        RMap<Long, CartProductDto> cartMap = redissonClient.getMap(KEY_PREFIX + request.getUserId());
        cartMap.remove(request.getItemId());
        DeleteCartItemResponse response = new DeleteCartItemResponse();
        response.setCode(ShoppingRetCode.SUCCESS.getCode());
        response.setMsg(ShoppingRetCode.SUCCESS.getMessage());
        return response;
    }

    @Override
    public DeleteCheckedItemResposne deleteCheckedItem(DeleteCheckedItemRequest request) {
        DeleteCheckedItemResposne resposne = new DeleteCheckedItemResposne();
        //拼接哈希表的key
        String key = KEY_PREFIX + request.getUserId();
        //获取RBatch进行批量执行
        RBatch batch = redissonClient.createBatch(BATCH_OPTIONS);
        RMapAsync<Long, CartProductDto> cartMap = batch.getMap(key);

//        错误的写法,无法获取数据会导致远程调用超时
//        Set<Map.Entry<Long, CartProductDto>> entries = cartMap.readAllEntrySetAsync().join();
//        batch.execute();

        //查询用户购物车
        RFuture<Set<Map.Entry<Long, CartProductDto>>> setRFuture = cartMap.readAllEntrySetAsync();
        batch.execute();
        Set<Map.Entry<Long, CartProductDto>> entries = setRFuture.join();

        //删除选中的商品
        batch = redissonClient.createBatch(BATCH_OPTIONS);
        cartMap = batch.getMap(key);
        for (Map.Entry<Long, CartProductDto> e : entries) {
            if (CHECKED_TRUE.equals(e.getValue().getChecked())) {
                cartMap.fastRemoveAsync(e.getKey());
            }
        }
        batch.execute();

        resposne.setCode(ShoppingRetCode.SUCCESS.getCode());
        resposne.setMsg(ShoppingRetCode.SUCCESS.getMessage());

        return resposne;
    }
}
